// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';

import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
import 'package:analysis_server/src/legacy_analysis_server.dart';
import 'package:analysis_server/src/plugin/result_merger.dart';
import 'package:analysis_server/src/request_handler_mixin.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
import 'package:analyzer_plugin/utilities/navigation/navigation_dart.dart';

/// The handler for the `analysis.getNavigation` request.
class AnalysisGetNavigationHandler extends LegacyHandler
    with RequestHandlerMixin<LegacyAnalysisServer> {
  /// Initialize a newly created handler to be able to service requests for the
  /// [server].
  AnalysisGetNavigationHandler(
      super.server, super.request, super.cancellationToken, super.performance);

  @override
  Future<void> handle() async {
    var params = AnalysisGetNavigationParams.fromRequest(request);
    var file = params.file;
    var offset = params.offset;
    var length = params.length;

    if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
      return;
    }

    var driver = server.getAnalysisDriver(file);
    if (driver == null) {
      sendResponse(Response.getNavigationInvalidFile(request));
    } else {
      //
      // Allow plugins to start computing navigation data.
      //
      var requestParams =
          plugin.AnalysisGetNavigationParams(file, offset, length);
      var pluginFutures =
          server.broadcastRequestToPlugins(requestParams, driver);
      //
      // Compute navigation data generated by server.
      //
      var allResults = <AnalysisNavigationParams>[];
      var result = await server.getResolvedUnit(file);
      if (result != null) {
        var collector = NavigationCollectorImpl();
        computeDartNavigation(
            server.resourceProvider, collector, result, offset, length);
        collector.createRegions();
        allResults.add(AnalysisNavigationParams(
            file, collector.regions, collector.targets, collector.files));
      }
      //
      // Add the navigation data produced by plugins to the server-generated
      // navigation data.
      //
      var responses = await waitForResponses(pluginFutures,
          requestParameters: requestParams);
      for (var response in responses) {
        var result = plugin.AnalysisGetNavigationResult.fromResponse(response);
        allResults.add(AnalysisNavigationParams(
            file, result.regions, result.targets, result.files));
      }
      //
      // Return the result.
      //
      var merger = ResultMerger();
      var mergedResults = merger.mergeNavigation(allResults);
      if (mergedResults == null) {
        sendResult(AnalysisGetNavigationResult(
            <String>[], <NavigationTarget>[], <NavigationRegion>[]));
      } else {
        sendResult(AnalysisGetNavigationResult(
            mergedResults.files, mergedResults.targets, mergedResults.regions));
      }
    }
  }
}
